import threading
import yaml
import json
import datetime
import pathlib
import time
import shutil
import subprocess
import os
import sys
import re
import requests
import psutil
import ctypes
import typing
import configparser
import zipfile
import sqlite3
import random
import logging
import cv2
import base64
import io
import PIL.Image

from selenium.webdriver.edge.webdriver import WebDriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.edge.service import Service
from selenium.webdriver.edge.options import Options
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.common.exceptions import *




class Custom_WebDriver:
    """单例类，用于管理 Edge 浏览器的 WebDriver 实例。

    Attributes:
        _instance (Custom_WebDriver): 类的单例实例。
        options (Options): Edge 浏览器的选项。
        service (Service): Edge 浏览器的服务。
        driver (WebDriver): Edge 浏览器的 WebDriver 实例。
        _element (WebElement): 当前操作的 WebElement 实例。
    """
    _instance = None

    def __new__(cls, existing_driver=None) -> 'Custom_WebDriver':
        """确保 Custom_WebDriver 只有一个实例。

        Args:
            existing_driver (WebDriver, optional): 已有的 WebDriver 实例。默认为 None。

        Returns:
            Custom_WebDriver: 类的单例实例。
        """
        if cls._instance is None:
            cls._instance = super(Custom_WebDriver, cls).__new__(cls)
            if existing_driver:
                cls._instance.driver = existing_driver
            else:
                cls._instance.options = Options()
                cls._instance.service = Service()
                cls._instance.options.add_experimental_option("debuggerAddress", "127.0.0.1:9652")
                cls._instance.driver = WebDriver(options=cls._instance.options)
            cls._instance.actions = ActionChains(cls._instance.driver)
            cls._instance.wait = WebDriverWait(driver=cls._instance.driver, timeout=10)
        return cls._instance

    def __init__(self, existing_driver=None) -> None:
        """初始化浏览器驱动类，设置选项和服务，并启动 WebDriver 实例。

        Args:
            existing_driver (WebDriver, optional): 已有的 WebDriver 实例。默认为 None。
        """
        if existing_driver:
            self.driver = existing_driver
        else:
            self.options = Options()
            self.service = Service()
            self.options.add_experimental_option("debuggerAddress", "127.0.0.1:9652")
            self.driver = WebDriver(options=self.options)

    def Goto_CurrentActivateUrl(self) -> 'Custom_WebDriver':
        """切换 Selenium 的 Handle 操作句柄到当前激活的页面

        Returns:
            Custom_WebDriver: 类对象本身
        """
        debug_json_data = requests.get(
            url="http://127.0.0.1:9652/json", timeout=5).json()

        # 判断获取回来的数据是否正确
        if not isinstance(debug_json_data, list):
            print("接口返回数据格式不正确（非列表或为空）")
            return None

        # 过来掉不需要的 page 页面
        filter_json_data = list(
                                    filter(lambda item: (
                                        item['type'] == "page" and
                                        not item['title'].lower().startswith("devtools") and
                                        not item['title'].lower().startswith("offscreen") and
                                        not item['title'].lower().startswith("新标签页") and
                                        not item['url'].lower().startswith("edge://newtab/") and
                                        not item['url'].lower().startswith("chrome-extension") and
                                        not item['url'].lower().startswith("chrome-devtools")
                                    ), debug_json_data)
                                )
        current_activate_handle = filter_json_data[0]['id']
        self.driver.switch_to.window(window_name=filter_json_data[0]['id'])
        return self

    def findelement(self, xpathvalue: str) -> 'Custom_WebDriver':
        """通过 XPath 查找单个元素，并将结果存储在实例变量 _element 中。

        Args:
            xpathvalue (str): 要查找的元素的 XPath 路径。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        try:
            self._element = self.driver.find_element(by=By.XPATH, value=xpathvalue)
            return self
        except NoSuchElementException:
            print(f"提示 : 无法找到目标元素!")
            return self
    
    def Implicitly_findelement(self, xpathvalue: str, timeout: float = 10) -> 'Custom_WebDriver':
        """通过 XPath 查找单个元素，如果在指定时间内未找到则打印提示信息。

        Args:
            xpathvalue (str): 要查找的元素的 XPath 路径。
            timeout (float): 查找元素的最大等待时间（秒）。

        Returns:
            Custom_WebDriver: 返回当前实例对象。
        """
        start_time = time.time()
        while time.time() - start_time < timeout:
            try:
                self._element = self.driver.find_element(by=By.XPATH, value=xpathvalue)
                return self
            except NoSuchElementException:
                time.sleep(0.5)  # 每隔0.5秒重试一次
        print(f"提示 : 无法找到目标元素!")
        return self

    def Explicit_visibility_findelement(self, xpathvalue: str) -> 'Custom_WebDriver':
        """安全地通过 XPath 查找单个可见元素，如果在指定时间内未找到则打印提示信息。

        Args:
            xpathvalue (str): 要查找的元素的 XPath 路径。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        try:
            self._element = WebDriverWait(driver=driver,timeout=10).until(expected_conditions.visibility_of_element_located(locator=(By.XPATH, xpathvalue)))
            return self
        except TimeoutException:
            print(f"提示 : 无法找到目标元素!")
            return self

    def Explicit_presence_findelement(self, xpathvalue: str) -> 'Custom_WebDriver':
        """安全地通过 XPath 查找单个DOM元素，如果在指定时间内未找到则打印提示信息。

        Args:
            xpathvalue (str): 要查找的元素的 XPath 路径。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        try:
            self._element = WebDriverWait(driver=driver,timeout=10).until(expected_conditions.presence_of_element_located(locator=(By.XPATH, xpathvalue)))
            return self
        except TimeoutException:
            print(f"提示 : 无法找到目标元素!")
            return self
    
    def Click(self) -> 'Custom_WebDriver':
        """点击当前选中的元素。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        self._element.click()
        return self

    def Script_Click(self) -> 'Custom_WebDriver':
        """使用 JavaScript 点击当前选中的元素。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        self.driver.execute_script("arguments[0].click();", self._element)
        return self

    def Script_Move_Click(self) -> 'Custom_WebDriver':
        """将当前选中的元素滚动到视图中并点击。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        self.driver.execute_script("arguments[0].scrollIntoView();", self._element)
        self.driver.execute_script("arguments[0].click();", self._element)
        return self

    def Safe_Click(self) -> 'Custom_WebDriver':
        _element : WebElement = self._element
        _scrollElement : WebElement = _element
        try:
            driver.execute_script("arguments[0].scrollIntoView({block: 'center', inline: 'center'});",_scrollElement)
            try:
                _element.click()
            except (ElementClickInterceptedException, ElementNotInteractableException):
                driver.execute_script("arguments[0].click();", _element)
        except:
            print("Unable To Execute Click!")
        
    def send_keys(self, keys: str) -> 'Custom_WebDriver':
        """向当前选中的元素发送按键。

        Args:
            keys (str): 要发送的按键。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        self._element.send_keys(keys)
        return self

    def Clean_Input(self, keys: str,attribute : str = "value") -> 'Custom_WebDriver':
        """向当前选中的元素发送按键，并清空输入框中的内容。

        Args:
            keys (str): 要发送的按键。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        
        _element : WebElement = self._element
        _scrollElement : WebElement = _element
        _startTime = datetime.datetime.now()
        _seconds = 5
        _elementAttribute = "value"
        try:
            driver.execute_script("arguments[0].scrollIntoView({block: 'center', inline: 'center'});",_scrollElement)
            try:
                _element.click()
            except (ElementClickInterceptedException, ElementNotInteractableException):
                driver.execute_script("arguments[0].click();", _element)
        except:
            print("Unable To Execute Click To Focus!")
        
        if _element.get_attribute(_elementAttribute) != "":
            while _startTime + datetime.timedelta(seconds=_seconds) > datetime.datetime.now():
                _element.send_keys(Keys.BACKSPACE*20)
                if _element.get_attribute(_elementAttribute) == "":
                    break
        return self

    def Safe_SendKeys(self,value,attribute : str = "value") -> 'Custom_WebDriver':
        _element : WebElement = self._element
        _scrollElement : WebElement = _element
        _startTime = datetime.datetime.now()
        _seconds = 5
        _elementAttribute = "value"
        _elementAttribute_value = value
        try:
            driver.execute_script("arguments[0].scrollIntoView({block: 'center', inline: 'center'});",_scrollElement)
            try:
                _element.click()
            except (ElementClickInterceptedException, ElementNotInteractableException):
                driver.execute_script("arguments[0].click();", _element)
        except:
            print("Unable To Execute Click To Focus!")
        
        if _element.get_attribute(_elementAttribute) != _elementAttribute_value:
            while _startTime + datetime.timedelta(seconds=_seconds) > datetime.datetime.now():
                _element.send_keys(Keys.BACKSPACE*20)
                if _element.get_attribute(_elementAttribute) == "":
                    break
            _element.send_keys(_elementAttribute_value)
        return self

    def delay(self, times: float) -> 'Custom_WebDriver':
        """暂停执行指定的时间。

        Args:
            times (float): 暂停的时间（秒）。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        time.sleep(times)
        return self

    def Script_Move_LeftTop(self, Offset_X: int = 0, Offset_Y: int = 0) -> 'Custom_WebDriver':
        """将页面滚动到当前选中元素的左上角偏移指定位置。

        Args:
            Offset_X (int): 水平偏移量。
            Offset_Y (int): 垂直偏移量。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        Element_Local_X = self._element.location['x']
        Element_Local_Y = self._element.location['y']
        self.driver.execute_script(f"window.scrollTo({Element_Local_X - Offset_X},{Element_Local_Y - Offset_Y});")
        return self

    def Script_MoveCenter(self) -> 'Custom_WebDriver':
        """将页面滚动到当前选中元素的中心位置。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        self.driver.execute_script("arguments[0].scrollIntoView({block: 'center', inline: 'center'});", self._element)
        return self

    def Hover(self) -> 'Custom_WebDriver':
        """将鼠标悬停在当前选中的元素上。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        ActionChains(driver=driver).move_to_element(self._element).perform()
        return self
    
    # def Script_Hover(self) -> 'Custom_WebDriver':
    def Script_Hover(self, duration: float = 0.5, offset_x: int = 0, offset_y: int = 0) -> 'Custom_WebDriver':
        """
        增强版 JavaScript 悬停
        
        Args:
            driver: WebDriver 实例
            element: 要悬停的元素
            duration: 悬停持续时间（秒）
            offset_x: X 轴偏移量
            offset_y: Y 轴偏移量
        """
        js_script = """
        // 创建鼠标悬停事件
        function simulateHover(element, duration, offsetX, offsetY) {
            // 计算元素位置（带偏移）
            const rect = element.getBoundingClientRect();
            const centerX = rect.left + rect.width / 2 + offsetX;
            const centerY = rect.top + rect.height / 2 + offsetY;
            
            // 创建鼠标移动事件
            const mouseMoveEvent = new MouseEvent('mousemove', {
                view: window,
                bubbles: true,
                cancelable: true,
                clientX: centerX,
                clientY: centerY
            });
            
            // 创建鼠标悬停事件
            const mouseOverEvent = new MouseEvent('mouseover', {
                view: window,
                bubbles: true,
                cancelable: true,
                clientX: centerX,
                clientY: centerY
            });
            
            // 创建鼠标进入事件
            const mouseEnterEvent = new MouseEvent('mouseenter', {
                view: window,
                bubbles: true,
                cancelable: true,
                clientX: centerX,
                clientY: centerY
            });
            
            // 分派事件
            document.dispatchEvent(mouseMoveEvent);
            element.dispatchEvent(mouseOverEvent);
            element.dispatchEvent(mouseEnterEvent);
            
            // 如果需要持续悬停
            if (duration > 0) {
                return new Promise(resolve => {
                    setTimeout(resolve, duration * 1000);
                });
            }
        }
        
        // 执行悬停
        const promise = simulateHover(arguments[0], arguments[1], arguments[2], arguments[3]);
        
        // 如果返回了Promise，等待它完成
        if (promise) {
            const done = arguments[arguments.length - 1];
            promise.then(done);
        }
        """
        
        # 执行脚本并等待完成
        self.driver.execute_async_script(js_script, self._element, duration, offset_x, offset_y)
        return self

    def Capture(self) -> 'Custom_WebDriver':
        """截取当前页面的截图。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        screenshot_base64 = self.driver.get_screenshot_as_base64()
        image_data = base64.b64decode(screenshot_base64)
        image_stream = io.BytesIO(image_data)
        # self.image = PIL.Image.open()
        
        return self

    def Set_implicitly_wait(self, seconds: float = 5) -> 'Custom_WebDriver':
        """设置隐式等待时间。

        Args:
            seconds (float): 隐式等待的时间（秒）。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        self.driver.implicitly_wait(seconds)
        return self

    def Set_Current_Element(self, _element: WebElement) -> 'Custom_WebDriver':
        """设置当前操作的元素。

        Args:
            _element (WebElement): 要设置的 WebElement 实例。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        self._element = _element
        return self

    def Explicit_Wait_Visibili(self, xpathvalue: str) -> bool:
        """显式等待指定元素可见。

        Args:
            xpathvalue (str): 要查找的元素的 XPath 路径。

        Returns:
            bool: 如果元素可见返回 True，否则返回 False。
        """
        try:
            WebDriverWait(driver=driver,timeout=10).until(expected_conditions.visibility_of_element_located(locator=(By.XPATH, xpathvalue)))
            return True
        except TimeoutException:
            print(f"提示 : {xpathvalue}元素未加载成功,请检查错误!")
            return False

    def Explicit_Wait_Presence(self, xpathvalue: str) -> bool:
        """显式等待指定元素存在。

        Args:
            xpathvalue (str): 要查找的元素的 XPath 路径。

        Returns:
            bool: 如果元素存在返回 True，否则返回 False。
        """
        try:
            WebDriverWait(driver=driver,timeout=10).until(expected_conditions.presence_of_element_located(locator=(By.XPATH, xpathvalue)))
            return True
        except TimeoutException:
            print(f"提示 : {xpathvalue}元素未加载成功,请检查错误!")
            return False

    def element_exists(self, xpathvalue: str) -> bool:
        """判断指定 XPath 路径下的元素是否存在。

        Args:
            xpathvalue (str): 要查找的元素的 XPath 路径。

        Returns:
            bool: 如果元素存在返回 True，否则返回 False。
        """
        try:
            self.driver.find_element(by=By.XPATH, value=xpathvalue)
            return True
        except NoSuchElementException:
            return False

    # def wait_for_element_with_condition(self, xpathvalue: str, condition: callable) -> bool:
    #     """等待某个元素出现，并且该元素满足某个条件。

    #     Args:
    #         xpathvalue (str): 要查找的元素的 XPath 路径。
    #         condition (callable): 一个函数，接受 WebElement 作为参数，并返回布尔值。

    #     Returns:
    #         bool: 如果元素满足条件返回 True，否则返回 False。
    #     """
    #     try:
    #         element = WebDriverWait(driver=driver,timeout=10).until(expected_conditions.presence_of_element_located(locator=(By.XPATH, xpathvalue)))
    #         return condition(element)
    #     except TimeoutException:
    #         print(f"提示 : {xpathvalue}元素未加载成功,请检查错误!")
    #         return False

    # def wait_for_element_with_attribute(self, xpathvalue: str, attribute: str, value: str) -> bool:
    #     """等待某个元素出现，并且该元素具备某个属性值。

    #     Args:
    #         xpathvalue (str): 要查找的元素的 XPath 路径。
    #         attribute (str): 元素的属性名称。
    #         value (str): 属性的期望值。

    #     Returns:
    #         bool: 如果元素具备属性值返回 True，否则返回 False。
    #     """
    #     def condition(element):
    #         return element.get_attribute(attribute) == value

    #     return WebDriverWait(driver=driver,timeout=10)_for_element_with_condition(xpathvalue, condition)



    def switch_to_iframe(self, xpathvalue: str) -> 'Custom_WebDriver':
        """切换到指定的 iframe。

        Args:
            xpathvalue (str): 定位 iframe 的 XPath 字符串。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        self.wait_for_frame_to_be_available_and_switch_to_it(xpathvalue)
        return self

    def wait_for_frame_to_be_available_and_switch_to_it(self, xpathvalue: str) -> 'Custom_WebDriver':
        """显式等待 iframe 可用并切换到该 iframe。

        Args:
            xpathvalue (str): 定位 iframe 的 XPath 字符串。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        try:
            iframe = WebDriverWait(driver=driver,timeout=10).until(expected_conditions.frame_to_be_available_and_switch_to_it((By.XPATH, xpathvalue)))
            return self
        except TimeoutException:
            print(f"提示 : {xpathvalue} iframe 未加载成功,请检查错误!")
            raise

    def exit_iframe(self) -> 'Custom_WebDriver':
        """从当前 iframe 切换回默认内容。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        self.driver.switch_to.default_content()
        return self

    def prompt(self, times: float=0.2) -> 'Custom_WebDriver':
        """为当前选中的元素添加红色边框以突出显示。

        Args:
            times (float): 暂停的时间（秒）。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        src_style_value = self._element.get_attribute("style")
        new_style_value = f"{src_style_value}; border: 5px solid red;" if src_style_value else "border: 5px solid red;"
        self.driver.execute_script("arguments[0].setAttribute('style', arguments[1]);", self._element, new_style_value)
        time.sleep(times)
        self.driver.execute_script("arguments[0].setAttribute('style', arguments[1]);", self._element, src_style_value)
        return self

    def find_uploadinput_by_label(self,label: str,max_depth: int = 5) -> 'Custom_WebDriver':
        """通过标签文本查找关联的输入框元素"""
        anchors = self.driver.find_elements(by=By.XPATH,value=f"//*[(self::span or self::label or self::div) and text()='{label}']")
        
        if len(anchors) != 1:
            print(f"找到 {len(anchors)} 个匹配标签，需要唯一标签")
            return None
            
        current_element = anchors[0]
        xpath_pattern = "./parent::*//input[@type='file']"
        
        for _ in range(max_depth):
            try:
                return current_element.find_element(by=By.XPATH, value=xpath_pattern)
            except NoSuchElementException:
                print(f"[yellow]当前层级未找到输入框，向上查找...")
                current_element = current_element.find_element(by=By.XPATH, value="./parent::*")
        
        print(f"[red]达到最大查找深度，未找到输入框")
        return self
    
    

if __name__ == "__main__":
    print("提示 : 模块测试")
    driver = Custom_WebDriver()
    driver.Goto_CurrentActivateUrl()
    
    
    # 1.修正 find_element
    # 2.增加 input 上传文件的功能.
    # 3.给类添加日志功能.
    # 4.日志功能必须支持使用 DEBUG 模式.
    # 5.将 by_near_text_find_element 添加进去
    # 6.将 with_condition_find_element 添加进去